package roborally.model;

import be.kuleuven.cs.som.annotate.*;

/**
 * A class of energy amounts expressing an amount of energy
 * in watt-second (Ws).
 * 	
 * @invar   ...
 *        	| isValidAmountInUnits(getAmountInUnits())
 * 
 * @version  1.0
 * @author	Pieter Robberechts	(1e Bach Informatica)
 * @author	Jeroen Sanders 		(1e Bach Informatica)
 */
public class EnergyAmount {

	/**
	 * Initialize this new energy amount with given amount in units.
	 * 
	 * @param  	amountInUnits
	 *         	The amount in units for this new energy amount.
	 * @pre	   	...
	 * 		 	| isValidAmountInUnits(anmountInUnits)	
	 * @effect 	...
	 *       	| setAmountInUnits(amountInUnits)
	 */
	@Raw
	public EnergyAmount(double amountInUnits) {
		assert(isValidAmountInUnits(amountInUnits));
		setAmountInUnits(amountInUnits);
	}

	/**
	 * Initialize this new energy amount to 0 Ws.
	 * 
	 * @effect 	...
	 *       	| this(0)
	 */
	@Raw
	public EnergyAmount() {
		this(0);
	}

	/**
	 * Return the amount in units of this energy amount.
	 */
	@Basic
	@Raw
	public double getAmountInUnits() {
		return this.amountInUnits;
	}

	/**
	 * Check whether the given amount is a valid amount in units
	 * for any energy amount.
	 * 
	 * @param  	amount
	 *         	The amount to be checked.
	 * @return 	...
	 *       	| result == ( amount >= 0 && amount <= Double.MAX_VALUE )
	 */
	public static boolean isValidAmountInUnits(double amount) {
		return amount >= 0 && amount <= Double.MAX_VALUE;
	}

	/**
	 * Set the amount in units for this energy amount to the given amount
	 * in units.
	 * 
	 * @param  	amountInUnits
	 *         	The new amount in units for this energy amount.
	 * @pre    	...
	 * 		 	| isValidAmountInUnits(amountInUnits)        
	 * @post   	...
	 *       	| new.getAmountInUnits().equals(amountInUnits)
	 */
	@Raw
	public void setAmountInUnits(double amountInUnits) {
		assert( isValidAmountInUnits(amountInUnits) );
		this.amountInUnits = amountInUnits;
	}

	/**
	 * Variable referencing the amount in units involved in
	 * this energy amount.
	 */
	private double amountInUnits;

	/**
	 * Change the value of this energy amount to the given energy
	 * amount.
	 * 
	 * @param  	target
	 *         	The energy amount to be registered. 
	 * @pre	   	...
	 * 		 	| target != null
	 * @effect 	...
	 *       	| setAmountInUnits(target.getAmountInUnits())
	 */
	public void changeValue(EnergyAmount target) {
		assert( target != null );
		setAmountInUnits(target.getAmountInUnits());
	}

	/**
	 * Add the given energy amount to this energy amount.
	 * 
	 * @param  	other
	 *         	The energy amount to add.
	 * @pre    	...
	 *        	| other != null
	 * @pre		...
	 * 			| isValidAmountInUnits(this.getAmountInUnits() + other.getAmountInUnits())       
	 * @return 	...
	 *       	| result == new EnergyAmount(getAmountInUnits() + other.getAmountInUnits())
	 */
	public EnergyAmount add(EnergyAmount other) {
		assert( other != null );
		assert( isValidAmountInUnits(this.getAmountInUnits() + other.getAmountInUnits()) );
		return new EnergyAmount(this.getAmountInUnits() + other.getAmountInUnits());
	}
	
	/**
	 * Subtract the given energy amount from this energy amount.
	 * 
	 * @param  	other
	 *         	The energy amount to subtract.
	 * @pre    	...
	 *        	| other != null
	 * @pre		...
	 * 			| isValidAmountInUnits(this.getAmountInUnits() - other.getAmountInUnits())      
	 * @return 	...
	 *       	| result == new EnergyAmount(getAmountInUnits() + other.getAmountInUnits())
	 */
	public EnergyAmount subtract(EnergyAmount other) {
		assert( other != null );
		assert( isValidAmountInUnits(this.getAmountInUnits() - other.getAmountInUnits()) );
		return new EnergyAmount(this.getAmountInUnits() - other.getAmountInUnits());
	}
	
	/**
	 * Check whether this energy amount is less than the given energy amount.
	 * 
	 * @param  	other
	 *         	The energy amount to compare with.
	 * @pre    	...
	 *        	| other != null    
	 * @return 	...
	 *       	| result == (this.getAmountInUnits() < other.getAmountInUnits())
	 */
	public boolean lessThan(EnergyAmount other) {
		assert( other != null );
		return (this.getAmountInUnits() < other.getAmountInUnits());
	}
	
	/**
	 * Check whether this energy amount is more than the given energy amount.
	 * 
	 * @param  	other
	 *         	The energy amount to compare with.
	 * @pre    	...
	 *        	| other != null    
	 * @return 	...
	 *       	| result == (this.getAmountInUnits() > other.getAmountInUnits())
	 */
	public boolean moreThan(EnergyAmount other) {
		assert( other != null );
		return (this.getAmountInUnits() > other.getAmountInUnits());
	}
	

	/**
	 * Return a new energy amount that is identical to this energy amount.
	 * 
	 * @return 	...
	 *       	| (result != null) && (result != this) &&
	 *       	| (result.getAmountInUnits() == this.getAmountInUnits())
	 */
	public EnergyAmount getCopy() {
		return new EnergyAmount(getAmountInUnits());
	}

	/**
	 * Check whether this energy amount is identical to the
	 * given energy amount.
	 * 
	 * @param  	other
	 *         	The energy amount to compare with.
	 * @return 	...
	 *       	| result ==
	 *       	|   (other != null) &&
	 *       	|   (getAmountInUnits() == other.getAmountInUnits())
	 */
	public boolean isIdenticalTo(EnergyAmount other) {
		return (other != null) && (getAmountInUnits() == other.getAmountInUnits());
	}

	/**
	 * Return a textual representation of this energy amount.
	 * 
	 * @return 	...
	 *       	| result.equals( getAmountInUnits() + " Ws" )
	 */
	public String toString() {
		return getAmountInUnits() + " Ws";
	}
}
